package com.ouc.pampas;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.Sets;
import io.terminus.common.redis.dao.RedisBaseDao;
import io.terminus.common.redis.utils.JedisTemplate;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;

@Repository
public class AppDao extends RedisBaseDao<App> {
	public AppDao() {
		this(null);
	}

	@Autowired(required = false)
	public AppDao(JedisTemplate template) {
		super(template);
	}

	@SuppressWarnings("unchecked")
	public App findByKey(final String key) {
		return (App) this.template.execute(new JedisTemplate.JedisAction() {
			public App action(Jedis jedis) {
				Map<String, String> hash = jedis.hgetAll(AppDao.key(key));
				return (App) AppDao.this.stringHashMapper.fromHash(hash);
			}
		});
	}

	public App findByDomain(final String domain) {
		String key = (String) this.template.execute(new JedisTemplate.JedisAction() {
			public String action(Jedis jedis) {
				return jedis.get(AppDao.keyByDomain(domain));
			}
		});
		if (key == null) {
			return null;
		}
		return findByKey(key);
	}

	public List<App> listAll() {
		Set<String> keys = (Set) this.template.execute(new JedisTemplate.JedisAction() {
			public Set<String> action(Jedis jedis) {
				return jedis.smembers(AppDao.keyAll());
			}
		});
		return findByKeys(keys, new Function() {
			public String apply(Object key) {
				return AppDao.key((String) key);
			}
		});
	}

	public void create(final App app) {
		this.template.execute(new JedisTemplate.JedisActionNoResult() {
			public void action(Jedis jedis) {
				Transaction t = jedis.multi();
				t.hmset(AppDao.key(app.getKey()), AppDao.this.stringHashMapper.toHash(app));
				t.setnx(AppDao.keyByDomain(app.getDomain()), app.getKey());
				t.sadd(AppDao.keyAll(), new String[] { app.getKey() });
				t.exec();
			}
		});
	}

	public void update(final App app) {
		final App exists = findByKey(app.getKey());
		this.template.execute(new JedisTemplate.JedisActionNoResult() {
			public void action(Jedis jedis) {
				Transaction t = jedis.multi();
				t.hmset(AppDao.key(app.getKey()), AppDao.this.stringHashMapper.toHash(app));
				if ((!Strings.isNullOrEmpty(app.getDomain()))
						&& (!Objects.equal(app.getDomain(), exists.getDomain()))) {
					t.del(AppDao.keyByDomain(exists.getDomain()));
					t.setnx(AppDao.keyByDomain(app.getDomain()), app.getKey());
				}
				t.exec();
			}
		});
	}

	public void updateExtraDomains(final String appKey, final Set<String> extraDomains) {
		this.template.execute(new JedisTemplate.JedisActionNoResult() {
			public void action(Jedis jedis) {
				Set<String> existsDomains = (Set) MoreObjects
						.firstNonNull(jedis.smembers(AppDao.keyExtraDomains(appKey)), Sets.newHashSet());
				Transaction t = jedis.multi();
				for (String domain : extraDomains) {
					if (existsDomains.contains(domain)) {
						existsDomains.remove(domain);
					} else {
						t.setnx(AppDao.keyByDomain(domain), appKey);
						t.sadd(AppDao.keyExtraDomains(appKey), new String[] { domain });
					}
				}
				for (String domain : existsDomains) {
					t.del(AppDao.keyByDomain(domain));
					t.srem(AppDao.keyExtraDomains(appKey), new String[] { domain });
				}
				t.exec();
			}
		});
	}

	public Set<String> getExtraDomains(final String appKey) {
		return (Set) this.template.execute(new JedisTemplate.JedisAction() {
			public Set<String> action(Jedis jedis) {
				return jedis.smembers(AppDao.keyExtraDomains(appKey));
			}
		});
	}

	public void delete(final String appKey) {
		final App exists = findByKey(appKey);
		if (exists == null) {
			return;
		}
		this.template.execute(new JedisTemplate.JedisActionNoResult() {
			public void action(Jedis jedis) {
				Transaction t = jedis.multi();
				t.del(AppDao.key(appKey));
				t.del(AppDao.keyByDomain(exists.getDomain()));
				t.srem(AppDao.keyAll(), new String[] { appKey });
				t.exec();
			}
		});
	}

	public static String keyAll() {
		return "apps";
	}

	public static String key(String key) {
		return "app:" + key;
	}

	public static String keyExtraDomains(String key) {
		return "app:" + key + ":extra-domains";
	}

	public static String keyByDomain(String domain) {
		return "app-domain:" + domain;
	}
}
